package data

import (
	"context"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/google/wire"
	"github.com/redis/go-redis/v9"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"transfer-service/internal/biz"
	"transfer-service/internal/conf"
	pdata "transfer/pkg/data"
)

// DataProviderSet is data providers.
var DataProviderSet = wire.NewSet(
	NewData,
	NewTransaction,
)

// Data .
type Data struct {
	db    *gorm.DB
	redis *redis.Client
}

// 用来承载事务的上下文
type contextTxKey struct{}

// NewTransaction .
func NewTransaction(d *Data) biz.Transaction {
	return d
}

// ExecTx po Transaction
func (d *Data) ExecTx(ctx context.Context, fn func(ctx context.Context) error) error {
	return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		ctx = context.WithValue(ctx, contextTxKey{}, tx)
		return fn(ctx)
	})
}

// DB 根据此方法来判断当前的 db 是不是使用 事务的 DB
func (d *Data) DB(ctx context.Context) *gorm.DB {
	tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
	if ok {
		return tx
	}
	return d.db
}

func NewDB(conf *conf.Bootstrap, logger log.Logger) (*gorm.DB, error) {
	logHelper := log.NewHelper(logger)
	c := conf.GetData()
	var db *gorm.DB
	var err error
	switch c.Database.Driver {
	case "postgres":
		db, err = gorm.Open(postgres.Open(c.Database.Source), &gorm.Config{})
		if err != nil {
			logHelper.Fatalf("failed opening connection to postgres: %v", err)
		}
	case "mysql":
		db, err = gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{})
		if err != nil {
			logHelper.Fatalf("failed opening connection to mysql: %v", err)
		}
	}
	logHelper.Info(c.Database.Driver + "数据库连接成功")
	return db, err
}

// NewRedis 初始化Redis连接
func NewRedis(conf *conf.Bootstrap, logger log.Logger) *redis.Client {
	logHelper := log.NewHelper(logger)
	c := conf.GetData()
	rdb := redis.NewClient(&redis.Options{
		Addr:         c.Redis.Addr,
		Password:     c.Redis.Password,
		DB:           int(c.Redis.Db),
		DialTimeout:  c.Redis.DialTimeout.AsDuration(),
		WriteTimeout: c.Redis.WriteTimeout.AsDuration(),
		ReadTimeout:  c.Redis.ReadTimeout.AsDuration(),
	})
	logHelper.Info("redis连接成功")
	return rdb
}

func NewData(conf *conf.Bootstrap, logger log.Logger) (*Data, func(), error) {
	logHelper := log.NewHelper(logger)
	db, err := NewDB(conf, logger)
	if err != nil {
		return nil, nil, err
	}
	rdb := NewRedis(conf, logger)
	pdata.InitBaseMapper(db)
	d := &Data{
		db:    db,
		redis: rdb,
	}
	return d, func() {
		logHelper.Info("message", "closing the data resources")
		sqlDB, err := db.DB()
		if err != nil {
			logHelper.Error(err)
		}
		//关闭数据库
		err = sqlDB.Close()
		if err != nil {
			logHelper.Error(err)
		}
		//关闭redis
		err = rdb.Close()
		if err != nil {
			logHelper.Error(err)
		}
	}, nil
}
